/*    file: UserAgent.cpp
 *    desc:
 * 
 * created: 2015-11-09
 *  author: chuanjiang.zh@qq.com
 * company: 
 */

#include "stdafx.h"
#include "UserAgent.h"



#include <resip/stack/ShutdownMessage.hxx>
#include <resip/stack/ExtensionHeader.hxx>
#include <resip/stack/Helper.hxx>
#include <resip/stack/PlainContents.hxx>

#include <resip/dum/MasterProfile.hxx>
#include <resip/dum/InMemorySyncRegDb.hxx>
#include <resip/dum/ClientAuthManager.hxx>
#include <resip/dum/ServerRegistration.hxx>
#include <resip/dum/ServerAuthManager.hxx>
#include <resip/dum/UserAuthInfo.hxx>
#include <resip/dum/KeepAliveManager.hxx>

#include <resip/dum/ClientInviteSession.hxx>
#include <resip/dum/ServerInviteSession.hxx>
#include <resip/dum/ClientRegistration.hxx>
#include <resip/dum/AppDialogSet.hxx>
#include <resip/dum/AppDialog.hxx>

#include <resip/dum/ServerOutOfDialogReq.hxx>

#include <rutil/MD5Stream.hxx>
#include "Utf8Encoder.h"

#include "ResipUtil.h"
#include "TFileUtil.h"
#include "TStringUtil.h"
#include "IniFile.h"

#include "CMediaPort.h"
#include "RecvMediaPort.h"
#include "SendMediaPort.h"

#include "UserAgentMessage.h"
#include "CSipMessage.h"
#include "Path.h"
#include "Utility.h"
#include "BasicMacro.h"


#define RESIPROCATE_SUBSYSTEM Subsystem::APP

int UserAgent::m_iSeq = 1;
static unsigned int MaxRegistrationRetryTime = 60;              // RFC5626 section 4.5 default
static unsigned int BaseRegistrationRetryTimeAllFlowsFailed = 30; // RFC5626 section 4.5 default





UserAgent::UserAgent():
    m_stack(),
    m_profile(new MasterProfile()),
    m_shuttingDown(),
    m_regRetryDelayTime()
{
    m_pollGroup.reset(FdPollGrp::create());
    m_interruptor.reset(new EventThreadInterruptor(*m_pollGroup));
    m_stackThread.reset(new EventStackThread(m_stack, *m_interruptor, *m_pollGroup));
    m_dum.reset(new DialogUsageManager(m_stack));

    m_inviteWithSdp = true;
    m_wnd = NULL;
    m_msg = 0;
}

UserAgent::~UserAgent()
{
    m_stack.shutdownAndJoinThreads();
    m_stackThread->shutdown();
    m_stackThread->join();

    m_dum.reset();
    m_profile.reset();

    m_stackThread.reset();
}

AppConfig& UserAgent::config()
{
    return AppConfig::instance();
}

bool UserAgent::loadConfig(const char* filename)
{
    return config().load(filename);
}


int UserAgent::startup()
{
    setupMasterProfile();
    setupDum();

    if (config().getBool("Agent.TCP", true))
    {
        m_stack.addTransport(TCP, getLocalPort());
    }

    if (config().getBool("Agent.UDP", true))
    {
        m_stack.addTransport(UDP, getLocalPort());
    }

    findApp();

    m_stack.run();

    m_stackThread->run();

    ThreadIf::run();

    return 0;
}

void UserAgent::shutdown()
{
    ThreadIf::shutdown();

    m_dum->post(new ShutdownMessage());

    join();
}

int UserAgent::getLocalPort()
{
    return config().getInt("Agent.Port", 3060);
}

std::string UserAgent::getLocalUser()
{
    return config().get("Agent.User", "SipShell");
}

std::string UserAgent::getHostAddr()
{
    std::string ip = m_dum->getHostAddress().c_str();
    return ip;
}

DialogUsageManager& UserAgent::getDum()
{
    return *m_dum;
}

NameAddr UserAgent::getLocalNameAddr()
{
    std::string ip = getHostAddr();
    int port = getLocalPort();

    NameAddr userAor;
    userAor.uri().host() = ip.c_str();
    userAor.uri().port() = port;
    userAor.uri().user() = getLocalUser().c_str();
    return userAor;
}

NameAddr UserAgent::getProxyAddr()
{
	std::string registar = config().get("Agent.Registar");
    NameAddr addr;
	if (registar.size() > 0)
	{
		addr = NameAddr(registar.c_str());
	}
    return addr;
}

std::string UserAgent::getContact()
{
    std::string ip = getHostAddr();
    int port = config().getInt("Agent.Port", 5060);
    return comn::StringUtil::format("sip:%s@%s:%d", getLocalUser().c_str(), ip.c_str(), port);
}

int UserAgent::getProxyPort()
{
    return config().getInt("Registar.Port", 5060);
}

bool UserAgent::isProxyMode()
{
    return config().getBool("Agent.ProxyMode", false);
}


std::string UserAgent::makeUri(const char* user)
{
    std::string uri(user);
    if (!comn::StringUtil::startsWith(uri, "sip"))
    {
        int port = getProxyPort();
        if (port == 5060)
        {
            uri = comn::StringUtil::format("sip:%s@%s", user,
                getProxyAddr().uri().host().c_str());
        }
        else
        {
            uri = comn::StringUtil::format("sip:%s@%s:%d", user,
                getProxyAddr().uri().host().c_str(), port);
        }
        
    }
    return uri;
}

void UserAgent::setupMasterProfile()
{
    NameAddr userAor;
    userAor.uri().host() = m_dum->getHostAddress();
    userAor.uri().port() = getLocalPort();
    userAor.uri().user() = getLocalUser().c_str();
    std::string password = config().get("Agent.Password", "123456");
    std::string realm = config().get("Agent.Realm", "");
    if (realm.empty())
    {
        realm = config().get("Registar.IP");
    }

    m_profile->setDefaultFrom(userAor);

    m_profile->setDigestCredential(realm.c_str(), userAor.uri().user(),
            password.c_str());

    m_profile->validateContentEnabled() = false;
    m_profile->validateAcceptEnabled() = false;
    m_profile->validateContentLanguageEnabled() = false;

    int sec = config().getInt("Agent.RegTime", 60);
    m_profile->setDefaultRegistrationTime(sec);
    m_profile->setDefaultMaxRegistrationTime(sec*10);

    m_profile->setDefaultSessionTime(100);
    m_profile->addSupportedOptionTag(Token("resource-priority"));
    m_profile->addSupportedOptionTag(Token(Symbols::Timer));

    m_profile->addSupportedMethod(resip::REGISTER);
    m_profile->addSupportedMethod(resip::INFO);
    m_profile->addSupportedMethod(resip::MESSAGE);
    m_profile->addSupportedMethod(resip::REFER);
    m_profile->addSupportedMethod(resip::PUBLISH);
    m_profile->addSupportedMethod(resip::SUBSCRIBE);
    m_profile->addSupportedMethod(resip::NOTIFY);
    m_dum->setMasterProfile(m_profile);
}

void UserAgent::setupUser()
{
	std::string section = "User";
	std::vector< std::string> keys = config().getKeys(section);
	for (size_t i = 0; i < keys.size(); ++i)
	{
		std::string key = keys[i];
		std::string path = section + AppConfig::SEPERATOR;
		path += key;

		std::string password;
		config().get(path, password);

		RegUser user;
		user.name = key;
		user.password = password;
		m_userStore.add(user);
	}
}

void UserAgent::setupDum()
{
	setupUser();

	std::vector< std::string > addrList;
	Utility::getHostAddr(addrList);
	for (size_t i = 0; i < addrList.size(); ++i)
	{
		m_dum->addDomain(addrList[i].c_str());
	}
	

	bool enableAuth = false;
	config().get("Agent.EnableAuth", enableAuth);

	std::string realm = config().get("Agent.Realm");
	if (realm.empty())
	{
		realm = config().get("Agent.IP");
	}

	m_authManager.reset(new RegUserAuthManager(*m_dum, m_userStore, realm.c_str()));
	if (enableAuth)
	{
		m_dum->setServerAuthManager(m_authManager);
	}

    m_regManager.reset(new InMemorySyncRegDb());
    m_dum->setRegistrationPersistenceManager(m_regManager.get());
    m_dum->setServerRegistrationHandler(this);

    std::auto_ptr<ClientAuthManager> clientAuth(new ClientAuthManager());
    m_dum->setClientAuthManager(clientAuth);

    std::auto_ptr<KeepAliveManager> keepAlive(new KeepAliveManager());
    KeepAliveManager::mKeepAlivePongTimeoutMs = 2;
    m_dum->setKeepAliveManager(keepAlive);

    m_dum->setClientRegistrationHandler(this);

    m_dum->setInviteSessionHandler(this);

    m_dum->setClientPagerMessageHandler(this);
    m_dum->setServerPagerMessageHandler(this);

    m_dum->addOutOfDialogHandler(OPTIONS, this);
}

void UserAgent::teardown()
{

}

void UserAgent::onDumCanBeDeleted()
{
    m_shuttingDown = true;
}


void UserAgent::test()
{
    Data user = "2036";
    Data password = "123456";
    Data realm = "172.16.14.210";
    Data nonce = "1434676499:c336b7a0f27e19a43f99b7a16defbe9f";

    resip::MD5Stream a1;
    a1 << user
        << resip::Symbols::COLON
        << realm
        << resip::Symbols::COLON
        << password;
    a1.flush();

    Data hex = a1.getHex();
    OutputDebugStringA(hex.c_str());
}

void UserAgent::thread()
{
    while (!isShutdown())
    {
        m_dum->process(1000);
    }

    m_dum->shutdown(this);

    teardown();
}


// server registration
/// Called when registration is refreshed
void UserAgent::onRefresh(ServerRegistrationHandle handle, const SipMessage& reg)
{
    handle->accept();
}

/// called when one or more specified contacts is removed
void UserAgent::onRemove(ServerRegistrationHandle handle, const SipMessage& reg)
{
    handle->accept();
}

/// Called when all the contacts are removed using "Contact: *"
void UserAgent::onRemoveAll(ServerRegistrationHandle handle, const SipMessage& reg)
{
    handle->accept();
}

/** Called when one or more contacts are added. This is after
    authentication has all succeeded */
void UserAgent::onAdd(ServerRegistrationHandle handle, const SipMessage& reg)
{
    handle->accept();
}

/// Called when a client queries for the list of current registrations
void UserAgent::onQuery(ServerRegistrationHandle, const SipMessage& reg)
{
    /// pass
}



// Registration Handler ////////////////////////////////////////////////////////
void UserAgent::onSuccess(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
{
    m_regHandle = h;

    if (::IsWindow(m_wnd))
    {
        PostMessage(m_wnd, m_msg, kAgentSession,
            (LPARAM)new SessionMsgData(kRegSuccess, "online"));
    }
}

void UserAgent::onFailure(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
{
    const StatusLine& statusline = response.header(StatusLineType());
    if (statusline.statusCode() == 401)
    {
        //h->requestRefresh();
    }
    
    WarningLog(<< "reg failed.");

    if (::IsWindow(m_wnd))
    {
        PostMessage(m_wnd, m_msg, kAgentSession,
            (LPARAM)new SessionMsgData(kRegFailure, "offline"));
    }
}

void UserAgent::onRemoved(resip::ClientRegistrationHandle h, const resip::SipMessage& response)
{
    InfoLog(<< "reg removed.");
}

int UserAgent::onRequestRetry(resip::ClientRegistrationHandle h, int retryMinimum, const resip::SipMessage& msg)
{
    m_regHandle = h;
    if (m_shuttingDown)
    {
        return -1;
    }

    if (m_regRetryDelayTime == 0)
    {
        m_regRetryDelayTime = BaseRegistrationRetryTimeAllFlowsFailed; // We only have one flow in this test app
    }

    unsigned int maxRetry = config().getInt("Agent.MaxRetryReg", MaxRegistrationRetryTime);

    // Use back off procedures of RFC 5626 section 4.5
    m_regRetryDelayTime = resipMin(MaxRegistrationRetryTime, m_regRetryDelayTime * 2);

    // return an evenly distributed random number between 50% and 100% of m_regRetryDelayTime
    int retryTime = Helper::jitterValue(m_regRetryDelayTime, 50, 100);
    InfoLog(
            << "onRequestRetry(ClientRegistrationHandle): msg=" << msg.brief() << ", retryTime=" << retryTime);

    return retryTime;
}

/// called when an initial INVITE or the intial response to an outoing invite
void UserAgent::onNewSession(ClientInviteSessionHandle handle, InviteSession::OfferAnswerType oat, const SipMessage& msg)
{
    DebugLog(<< "==> onNewSession msg=" << msg.brief() << "\n");

    //stopCall();

    //m_mediaPort.reset(createMediaPort());

    m_inviteSession = handle->getSessionHandle();

}

void UserAgent::onNewSession(ServerInviteSessionHandle handle, InviteSession::OfferAnswerType oat, const SipMessage& msg)
{
    DebugLog(<< "<== onNewSession msg=" << msg.brief() << "\n");

    stopAudioCall();

	SendMediaPort* mediaPort = new SendMediaPort();
	mediaPort->setOriginSdp(m_audioSdp);
	mediaPort->setPath(m_audioRtp);

	int packetSize = config().getInt("Audio.PacketSize", 80);
	mediaPort->setPacketSize(packetSize);
	
    m_audioPort.reset(mediaPort);

    m_audioInviteSession = handle->getSessionHandle();

    handle->provisional(100);

}

/// Received a failure response from UAS
void UserAgent::onFailure(ClientInviteSessionHandle handle, const SipMessage& msg)
{
    /// pass
    if (::IsWindow(m_wnd))
    {
        PostMessage(m_wnd, m_msg, kAgentSession, 
            (LPARAM)new SessionMsgData(kSessionFailure, "failed to call out"));
    }
}

/// called when an in-dialog provisional response is received that contains a body
void UserAgent::onEarlyMedia(ClientInviteSessionHandle handle, const SipMessage&, const SdpContents&)
{
    /// pass
}

void UserAgent::onEarlyMedia(ClientInviteSessionHandle handle, const SipMessage&, const Contents&)
{
    /// pass
}


/// called when dialog enters the Early state - typically after getting 18x
void UserAgent::onProvisional(ClientInviteSessionHandle handle, const SipMessage&)
{
    /// pass
}


/// called when a dialog initiated as a UAC enters the connected state
void UserAgent::onConnected(ClientInviteSessionHandle handle, const SipMessage& msg)
{
    if (m_mediaPort)
    {
        m_mediaPort->start();
    }

    if (::IsWindow(m_wnd))
    {
        resip::NameAddr peerAddr = handle->peerAddr();
        std::string text = comn::StringUtil::format("call out: %s", peerAddr.uri().getAor().c_str());
        PostMessage(m_wnd, m_msg, kAgentSession, 
            (LPARAM)new SessionMsgData(kSessionConnected, text));
    }
}


/// called when a dialog initiated as a UAS enters the connected state
void UserAgent::onConnected(InviteSessionHandle handle, const SipMessage& msg)
{
	if (m_audioInviteSession.isValid())
	{
		if (m_audioPort)
		{
			m_audioPort->start();
		}
	}
	else
	{
		if (m_mediaPort)
		{
			m_mediaPort->start();
		}
	}

	if (::IsWindow(m_wnd))
	{
		resip::NameAddr peerAddr = handle->peerAddr();
		std::string text = comn::StringUtil::format("call in: %s", peerAddr.uri().getAor().c_str());

		PostMessage(m_wnd, m_msg, kAgentSession, 
			(LPARAM)new SessionMsgData(kSessionConnected, text));
	}
}


void UserAgent::onTerminated(InviteSessionHandle handle, InviteSessionHandler::TerminatedReason reason, const SipMessage* related)
{
	if (m_audioPort)
	{
		m_audioPort->stop();
		m_audioPort.reset();
	}

    if (m_mediaPort)
    {
        m_mediaPort->stop();
        m_mediaPort.reset();
    }

    if (::IsWindow(m_wnd))
    {
        resip::NameAddr peerAddr = handle->peerAddr();
        std::string text = comn::StringUtil::format("call terminated: %s", peerAddr.uri().getAor().c_str());

        PostMessage(m_wnd, m_msg, kAgentSession, 
            (LPARAM)new SessionMsgData(kSessionTerminated, text));
    }
}

void UserAgent::onForkDestroyed(ClientInviteSessionHandle handle)
{
    // pass
}

void UserAgent::onRedirected(ClientInviteSessionHandle handle, const SipMessage& msg)
{
    // pass
}

void UserAgent::onAnswer(InviteSessionHandle handle, const SipMessage& msg, const SdpContents& sdp)
{
    std::string strSDP = ResipUtil::toString(sdp);

    int rc = m_mediaPort->setPeerSdp(strSDP);
    if (rc != 0)
    {
        std::string reason = comn::StringUtil::format("failed to match sdp. rc=%d", rc);
        if (handle->isConnected())
        {
            handle->end(reason.c_str());
        }
        else
        {
            WarningCategory warning;
            warning.code() = rc;
            warning.text() = reason.c_str();
            handle->reject(400, &warning);
        }
    }
}

void UserAgent::onOffer(InviteSessionHandle handle, const SipMessage& msg, const SdpContents& sdp)
{
    DebugLog(<< "onOffer sdp -----\n" << sdp << std::endl);

    std::string strSDP = ResipUtil::toString(sdp);

	std::string sdpAnswer;
	if (m_audioPort)
	{
		sdpAnswer = m_audioPort->getPeerSdp(strSDP);
	}
	else
	{
		sdpAnswer = m_mediaPort->getPeerSdp(strSDP);
	}

	if (sdpAnswer.empty())
	{
		handle->reject(400);
		return;
	}

	HeaderFieldValue hfv(sdpAnswer.c_str(), sdpAnswer.size());
	static Mime type("application", "sdp");
	SdpContents answerContent(hfv, type);

	DebugLog(<< "make answer sdp -----\n" << sdpAnswer << std::endl);

	int ms = config().getInt("Agent.PreSendMedia");
	if (ms > 0)
	{
		m_mediaPort->start();
		::Sleep(ms);
	}

	handle->provideAnswer(answerContent);

	ServerInviteSession* session = dynamic_cast<ServerInviteSession*>(handle.get());
	if (session && !session->isAccepted())
	{
		session->accept(200);
	}
}

void UserAgent::onOfferRequired(InviteSessionHandle handle, const SipMessage& msg)
{
    std::string localSDP = m_mediaPort->getLocalSdp();

    HeaderFieldValue hfv(localSDP.c_str(), localSDP.size());
    Mime type("application", "sdp");
    SdpContents sdpOffer(hfv, type);

    ServerInviteSession* session = dynamic_cast<ServerInviteSession*>(handle.get());
    session->provideOffer(sdpOffer, true);
    if (!session->isAccepted())
    {
        session->accept(200);
    }
    
}

void UserAgent::onOfferRejected(InviteSessionHandle handle, const SipMessage* msg)
{
    /// pass
}

void UserAgent::onInfo(InviteSessionHandle handle, const SipMessage& msg)
{
    handle->acceptNIT(200);

    std::string fromUri = msg.header(h_From).uri().getAor().c_str();
    std::string mime = ResipUtil::getMime(msg);

    const char* buf = msg.getRawBody().getBuffer();
    std::string text;
    if (buf != NULL)
    {
        if (config().getBool("Agent.Utf-8", true))
        {
            Utf8Encoder utf8Encoder;
            text = utf8Encoder.utf8ToGbk(buf);
        }
        else
        {
            text = buf;
        }
    }

    onRecvMessage(fromUri, text, mime);
}

void UserAgent::onInfoSuccess(InviteSessionHandle handle, const SipMessage& msg)
{
    /// pass
    std::string mime = ResipUtil::getMime(msg);
    std::string text = msg.getRawBody().getBuffer();
    std::string toUri = msg.header(h_To).uri().getAor().c_str();
    onSendMessage(toUri, text, mime);
}

void UserAgent::onInfoFailure(InviteSessionHandle handle, const SipMessage& msg)
{
    /// pass
}


void UserAgent::onMessage(InviteSessionHandle handle, const SipMessage& msg)
{
    handle->acceptNIT();

    std::string fromUri = msg.header(h_From).uri().getAor().c_str();
    std::string mime = ResipUtil::getMime(msg);

    const char* buf = msg.getRawBody().getBuffer();
    std::string text;
    if (buf != NULL)
    {
        if (config().getBool("Agent.Utf-8", true))
        {
            Utf8Encoder utf8Encoder;
            text = utf8Encoder.utf8ToGbk(buf);
        }
        else
        {
            text = buf;
        }
    }

    onRecvMessage(fromUri, text, mime);
}

void UserAgent::onMessageSuccess(InviteSessionHandle handle, const SipMessage& msg)
{
    /// pass
    std::string mime = ResipUtil::getMime(msg);
    std::string text = msg.getRawBody().getBuffer();
    std::string toUri = msg.header(h_To).uri().getAor().c_str();
    onSendMessage(toUri, text, mime);
}

void UserAgent::onMessageFailure(InviteSessionHandle handle, const SipMessage& msg)
{
    /// pass
}

void UserAgent::onRefer(InviteSessionHandle handle, ServerSubscriptionHandle subHandle, const SipMessage& msg)
{
    /// pass
}


void UserAgent::onReferNoSub(InviteSessionHandle handle, const SipMessage& msg)
{
    /// pass
}


void UserAgent::onReferRejected(InviteSessionHandle handle, const SipMessage& msg)
{
    /// pass
}


void UserAgent::onReferAccepted(InviteSessionHandle handle, ClientSubscriptionHandle, const SipMessage& msg)
{
    /// pass
}


// Called when a MESSAGE has been successfully sent
void UserAgent::onSuccess(ClientPagerMessageHandle, const SipMessage& status)
{
    std::string mime = ResipUtil::getMime(status);
    std::string text = status.getRawBody().getBuffer();
    std::string toUri = status.header(h_To).uri().getAor().c_str();
    onSendMessage(toUri, text, mime);
}

// Application could re-page the failed contents or just ingore it.
void UserAgent::onFailure(ClientPagerMessageHandle, const SipMessage& status, std::auto_ptr<Contents> contents)
{
    std::string mime = ResipUtil::getMime(status);
    std::string text = status.getRawBody().getBuffer();
    std::string toUri = status.header(h_To).uri().getAor().c_str();
    onSendMessage(toUri, text, mime);
}

void UserAgent::onMessageArrived(ServerPagerMessageHandle handle, const SipMessage& message)
{
    SharedPtr<SipMessage> msg = handle->accept();
    m_dum->send(msg);

    std::string fromUri = message.header(h_From).uri().getAor().c_str();
    std::string mime = ResipUtil::getMime(message);
    
    const char* buf = message.getRawBody().getBuffer();
    std::string text;
    if (buf != NULL)
    {
        if (config().getBool("Agent.Utf-8", true))
        {
            Utf8Encoder utf8Encoder;
            text = utf8Encoder.utf8ToGbk(buf);
        }
        else
        {
            text = buf;
        }
    }
    
    onRecvMessage(fromUri, text, mime);
}


void UserAgent::onSuccess(ClientOutOfDialogReqHandle, const SipMessage& successResponse)
{

}

void UserAgent::onFailure(ClientOutOfDialogReqHandle, const SipMessage& errorResponse)
{

}

void UserAgent::onReceivedRequest(ServerOutOfDialogReqHandle handle, const SipMessage& request)
{
    handle->send(handle->accept());
}




void UserAgent::reg()
{
    unreg();

    NameAddr userAor = getProxyAddr();
	if (userAor.uri().host().size() > 0)
	{
	    userAor.uri().user() = config().get("Agent.User").c_str();

	    SharedPtr<SipMessage> regMessage = m_dum->makeRegistration(userAor);
		m_dum->send(regMessage);
	}
}

void UserAgent::unreg()
{
    if (m_regHandle.isValid())
    {
        m_regHandle->removeAll();
        m_regHandle = ClientRegistrationHandle();
    }
}

void UserAgent::call(const std::string& uri)
{
    stopCall();

    try 
    {
        m_mediaPort.reset(createMediaPort());

        NameAddr uaAddr(uri.c_str());
        std::string localSDP;

        std::auto_ptr< SdpContents >    sdp;
        if (m_inviteWithSdp)
        {
            localSDP = m_mediaPort->getLocalSdp();

            HeaderFieldValue hfv(localSDP.c_str(), localSDP.size());
            Mime type("application", "sdp");
            sdp.reset(new SdpContents(hfv, type));
        }

        SharedPtr<SipMessage> invite = m_dum->makeInviteSession(uaAddr, sdp.get(), NULL);

        if (isProxyMode() && (getHostAddr() != uaAddr.uri().host().c_str()))
        {
            NameAddr proxyAddr = getLocalNameAddr();
            invite->setForceTarget(proxyAddr.uri());
        }

        int expire = config().getInt("Agent.SessionExpire", -1);
        if (expire > 0)
        {
            invite->header(h_Supporteds).push_back(Token(Symbols::Timer));
            ExpiresCategory& expires = invite->header(h_SessionExpires);
            expires.value() = expire;
            expires.param(p_refresher) = "uac";
        }

        std::string priority = config().get("Agent.Resource-Priority");
        if (!priority.empty())
        {
            invite->header(h_Requires).push_back(Token("resource-priority"));
            resip::ExtensionHeader h_ResourcePriority("Resource-Priority");
            invite->header(h_ResourcePriority).push_back(StringCategory("cmd.3"));
        }

        m_dum->send(invite);
    }
    catch (...)
    {
        m_mediaPort.reset();
    }
}

void UserAgent::stopCall()
{
    if (m_mediaPort)
    {
        m_mediaPort->stop();
        m_mediaPort.reset();
    }

    if (m_inviteSession.isValid())
    {
        m_inviteSession->end();
    }
    
}

bool UserAgent::reinvite()
{
    if (!m_inviteSession.isValid())
    {
        return false;
    }

    std::string localSDP;
    localSDP = m_mediaPort->getLocalSdp();

    HeaderFieldValue hfv(localSDP.c_str(), localSDP.size());
    Mime type("application", "sdp");
    SdpContents sdp(hfv, type);

    m_inviteSession->provideOffer(sdp);

    return true;
}


void UserAgent::onSendMessage(const std::string& toUri, const std::string& xml,
                const std::string& mime)
{
    if (!::IsWindow(m_wnd))
    {
        return;
    }

    CSipMessage* msg = new CSipMessage();
    msg->m_out = true;
    msg->m_uri = toUri;
    msg->m_mime = mime;
    msg->m_text = xml;
    msg->m_method = "MESSAGE";

    PostMessage(m_wnd, m_msg, kAgentMessage, (LPARAM)msg);
}

void UserAgent::onRecvMessage(const std::string& fromUri, const std::string& xml,
                const std::string& mime)
{
    if (!::IsWindow(m_wnd))
    {
        return;
    }

    CSipMessage* msg = new CSipMessage();
    msg->m_out = false;
    msg->m_uri = fromUri;
    msg->m_mime = mime;
    msg->m_text = xml;
    msg->m_method = "MESSAGE";

    PostMessage(m_wnd, m_msg, kAgentMessage, (LPARAM)msg);
}

void UserAgent::sendMessage(const std::string& uri, const std::string& xml,
            const std::string& mime)
{
    NameAddr addr(uri.c_str());

    NameAddr proxyAddr = getLocalNameAddr();

    SharedPtr<SipMessage> msg = m_dum->makeOutOfDialogRequest(addr, resip::MESSAGE);
    msg->header(h_RequestLine).uri() = addr.uri();
	msg->header(h_CSeq).sequence() = m_iSeq++;
    if (isProxyMode())
    {
        msg->setForceTarget(proxyAddr.uri());
    }

    if (config().getBool("Agent.Utf-8", true))
    {
        static Utf8Encoder utf8Encoder;
        const char* utf8Xml = utf8Encoder.gbkToUtf8(xml);
        msg->setBody(utf8Xml, strlen(utf8Xml));
    }
    else
    {
        msg->setBody(xml.c_str(), xml.size());
    }

    ResipUtil::setMime(*msg, mime);


    if (!config().getBool("Agent.MessageWithContact", false))
    {
        msg->remove(Headers::Contact);
    }

    std::string strMsg = comn::StringCast::toString(*msg);

    onSendMessage(uri, xml, mime);

    m_dum->send(msg);
}

void UserAgent::sendMessage(const std::string& uri, const std::string& xml)
{
    sendMessage(uri, xml, "Application/MANSCDP+xml");
}

void UserAgent::sendDialogMessage(const std::string& xml, const std::string& mime)
{
    Data text(xml.c_str());
    Utf8Encoder utf8Encoder;
    if (config().getBool("Agent.Utf-8", true))
    {
        text = utf8Encoder.gbkToUtf8(xml);
    }

    PlainContents content(text, ResipUtil::makeMime(mime));
    if (m_inviteSession.isValid())
    {
        m_inviteSession->message(content);

        std::string uri = comn::StringCast::toString(m_inviteSession->remoteTarget().uri());
        onSendMessage(uri, xml, mime);
    }
}

void UserAgent::sendDialogMessage(const std::string& xml)
{
    sendDialogMessage(xml, "Application/command+xml");
}

bool UserAgent::info(const std::string& xml, const std::string& mime)
{
    if (!m_inviteSession.isValid())
    {
        return false;
    }

    PlainContents content(xml.c_str(), ResipUtil::makeMime(mime));
    m_inviteSession->info(content);


    std::string toUri = m_inviteSession->peerAddr().uri().getAor().c_str();
    onSendMessage(toUri, xml, mime);

    return true;
}

void UserAgent::options(const std::string& uri, const std::string& text, const std::string& mime)
{
    NameAddr addr = uri.empty() ? getProxyAddr() : NameAddr(uri.c_str());

    SharedPtr<SipMessage> msg = m_dum->makeOutOfDialogRequest(addr, resip::OPTIONS);

    if (!mime.empty())
    {
        msg->setBody(text.c_str(), text.size());
        ResipUtil::setMime(*msg, mime);
    }
    m_dum->send(msg);
}


void UserAgent::setInviteWithSdp(bool withSdp)
{
    m_inviteWithSdp = withSdp;
}

void UserAgent::setWindowMessage(HWND hwnd, UINT msg)
{
    m_wnd = hwnd;
    m_msg = msg;
}

void UserAgent::setOriginSdp(const char* sdp)
{
    m_sdp = sdp;

    if (m_mediaPort)
    {
        m_mediaPort->setOriginSdp(m_sdp);
    }
}

void UserAgent::findApp()
{
    std::string workdir = comn::Path::getWorkDir();
    std::string paths[] =
    {
        ".",
        "..",
        "./bin",
        "../bin",
        "../../bin",
        "../../tool",
        workdir,
        workdir + "../../tool/",
        workdir + "../../../tool/"
    };

    std::string player = Utility::findExeFile(paths, ARRAY_SIZE(paths), "ffplay.exe");

	std::string param = config().get("Ffplay.Param");

    m_playerPath = player + " " + param + " %s";

    m_rtpplayPath = Utility::findExeFile(paths, ARRAY_SIZE(paths), "rtpplay.exe");

    m_videoRtp = Utility::findFile(paths, ARRAY_SIZE(paths), "video.rtp");
    m_audioRtp = Utility::findFile(paths, ARRAY_SIZE(paths), "LocalAudio.pcm");

}

MediaPort* UserAgent::createMediaPort()
{
	RecvMediaPort* mediaPort = new RecvMediaPort();
    mediaPort->setOriginSdp(m_sdp);
    mediaPort->setPath(m_playerPath, "dump.ps");
    return mediaPort;
}

void UserAgent::stopAudioCall()
{
	if (m_audioPort)
	{
		m_audioPort->stop();
		m_audioPort.reset();
	}

	if (m_audioInviteSession.isValid())
	{
		m_audioInviteSession->end();
	}
}

void UserAgent::setOriginAudioSdp(const char* sdp)
{
	m_audioSdp = sdp;

	if (m_audioPort)
	{
		m_audioPort->setOriginSdp(m_sdp);
	}
}

